/////////////////////////////////////////////////////////////////
// FileBuffer.h
//
// Buffered file reading.
/////////////////////////////////////////////////////////////////

#ifndef FILEBUFFER_H
#define FILEBUFFER_H

#include <string>
#include <fstream>
#include <iostream>

using namespace std;

const int BufferSize = 1000;

/////////////////////////////////////////////////////////////////
// FileBuffer
//
// Class for buffering file reading.
/////////////////////////////////////////////////////////////////

class FileBuffer {
	ifstream file;
	char buffer[BufferSize];
	int currPos;
	int size;
	bool isEOF;
	bool isValid;
	bool canUnget;

public:

	// Some common routines

	FileBuffer(const char *filename) :
			file(filename), currPos(0), size(0), isEOF(false), isValid(
					!file.fail()), canUnget(false) {
	}
	~FileBuffer() {
		close();
	}
	bool fail() const {
		return !isValid;
	}
	bool eof() const {
		return (!isValid || isEOF);
	}
	void close() {
		file.close();
		isValid = false;
	}

	/////////////////////////////////////////////////////////////////
	// FileBuffer::Get()
	//
	// Retrieve a character from the file buffer.  Returns true if
	// and only if a character is read.
	/////////////////////////////////////////////////////////////////

	bool Get(char &ch) {

		// check to make sure that there's more stuff in the file
		if (!isValid || isEOF)
			return false;

		// if the buffer is empty, it's time to reload it
		if (currPos == size) {
			file.read(buffer, BufferSize);
			size = file.gcount();
			isEOF = (size == 0);
			currPos = 0;
			if (isEOF)
				return false;
		}

		// store the read character
		ch = buffer[currPos++];
		canUnget = true;
		return true;
	}

	/////////////////////////////////////////////////////////////////
	// FileBuffer::UnGet()
	//
	// Unretrieve the most recently read character from the file
	// buffer.  Note that this allows only a one-level undo.
	/////////////////////////////////////////////////////////////////

	void UnGet() {
		assert(canUnget);
		assert(isValid);
		assert(currPos > 0);
		currPos--;
		assert(currPos < size);
		isEOF = false;
		canUnget = false;
	}

	/////////////////////////////////////////////////////////////////
	// FileBuffer::GetLine()
	//
	// Retrieve characters of text until a newline character is
	// encountered.  Terminates properly on end-of-file condition.
	/////////////////////////////////////////////////////////////////

	void GetLine(string &s) {
		char ch;
		s = "";
		while (Get(ch) && ch != '\n')
			s += ch;
	}

};

#endif
